package com.bytefy.server.db;

import java.io.File;

import org.jgroups.ChannelClosedException;
import org.jgroups.ChannelException;
import org.jgroups.ChannelNotConnectedException;
import org.jgroups.ExtendedReceiverAdapter;
import org.jgroups.JChannel;
import org.jgroups.Message;
import org.jgroups.View;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.bytefy.core.ICallback;
import com.bytefy.core.WaitQuit;

public class Server implements Runnable {
	private Persistence db = new Persistence();
	private Logger log = LoggerFactory.getLogger(getClass());
	private Thread thread;
	private JChannel channel;

	public JChannel getChannel() {
		return channel;
	}

	public void setChannel(JChannel channel) {
		this.channel = channel;
	}

	private boolean quitIniciator = false;
	private Normalizer normalizer;

	public boolean isQuitIniciator() {
		return quitIniciator;
	}

	public void setQuitIniciator(boolean quitIniciator) {
		this.quitIniciator = quitIniciator;
	}

	public void run() {
		try {
			channel = new JChannel(new File("../../udp.xml"));
			channel.setReceiver(new ExtendedReceiverAdapter() {
				public void receive(Message msg) {
					if (msg.getSrc().equals(channel.getLocalAddress()))
						return;
					Command cmd = (Command) msg.getObject();

					log.info("Catch command: " + cmd.getName());
					if (Command.QUIT.equals(cmd.getName())) {
						Server.this.close();
					} else if (Command.PING.equals(cmd.getName())) {
						Server.this.send(new Command(Command.PONG));
					} else if (Command.NORMALIZE.equals(cmd.getName())) {
						Persistence.normalize();
					}
				}

				public void viewAccepted(View new_view) {
					// System.out.println("received view " + new_view);
				}
			});
			channel.connect("__db__");
			normalizer = new Normalizer();
			normalizer.start();
		} catch (ChannelException e) {
			e.printStackTrace();
		}
	}

	public void send(Command cmd) {
		Message m = new Message();
		m.setObject(cmd);
		try {
			channel.send(m);
		} catch (ChannelNotConnectedException e) {
			e.printStackTrace();
		} catch (ChannelClosedException e) {
			e.printStackTrace();
		}
	}

	public synchronized void close() {
		try {
			log.info("Quit...");
			db.close();
			log.info("Quit now!");
		} catch (Throwable t) {
			t.printStackTrace();
		}
		System.exit(0);
	}

	public void start() {
		thread = new Thread(this);
		thread.setDaemon(true);
		thread.start();
	}

	public void stop() {
		normalizer.interrupt();
		thread.interrupt();
		try {
			thread.join();
			normalizer.join();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

	public static void main(String[] args) {
		final Server server = new Server();
		server.start();
		ICallback<String> quit = new ICallback<String>() {
			public void call(String data) {
				server.close();
			}
		};
		ICallback<String> quitAll = new ICallback<String>() {
			public void call(String data) {
				server.send(new Command(Command.QUIT));
				try {
					Thread.sleep(300);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				server.close();
			}
		};
		ICallback<String> ping = new ICallback<String>() {
			public void call(String data) {
				server.setQuitIniciator(true);
				server.send(new Command(Command.PING));
			}
		};
		ICallback<String> state = new ICallback<String>() {
			public void call(String data) {
				System.out.println("view: " + server.getChannel().getView());
			}
		};
		final ICallback<String> normilize = new ICallback<String>() {
			public void call(String data) {
				Persistence.normalize();
			}
		};
		ICallback<String> normalizeAll = new ICallback<String>() {
			public void call(String data) {
				server.send(new Command(Command.NORMALIZE));
				normilize.call(null);
			}
		};
		WaitQuit wq = new WaitQuit();
		wq.addCallback("quit", quit);
		wq.addCallback("quitall", quitAll);
		wq.addCallback("ping", ping);
		wq.addCallback("state", state);
		wq.addCallback("normalize", normilize);
		wq.addCallback("normalizeall", normalizeAll);
		wq.loop();
	}
}